ASP.NET Universal Providers

Last update: 11 January 2012

The SqlMembershipProvider, SqlRoleProvider, SqlProfileProvider classes that shipped in ASP.NET through version 4 support only Microsoft SQL Server and Microsoft SQL Server Express. They do not support newer offerings such as Microsoft SQL Azure and Microsoft SQL Server Compact.

ASP.NET Universal Providers have been created in order to extend support to all editions of SQL Server 2005 and later and to SQL Azure. If you use these providers to develop your application, the application will be ready for cloud environments like Azure.

Other than supporting additional storage options, the providers work like the existing SQL-based providers. Except as noted below, using ASP.NET Universal Providers requires no change in any of your applications.

·         Installing and Configuring ASP.NET Universal Providers

·         Selecting a Data Store

·         Storing Data in Session State using ASP.NET Universal Providers

·         Known Issues

·         Additional Resources

·         Disclaimer

Installing and Configuring ASP.NET Universal Providers

To install ASP.NET Universal Providers, you use a NuGet package, which installs all required files (including this documentation). The NuGet package automatically enables the new providers when it is installed. By default, the NuGet package configures provider to use SQL Server Express. To use SQL Server Compact or SQL Azure, you must change the connection string for the provider, as explained later in this document.

To enable the providers, the NuGet package adds configuration entries in the web.config file. The configuration for these providers is the same as the existing SqlMembershipProvider class, but the type parameter is set to the type of the new providers, as shown in the following table:

SQL Provider Types

Equivalent Type for Universal Providers

System.Web.Security.SqlMembershipProvider

System.Web.Providers.DefaultMembershipProvider

System.Web.Profile.SqlProfileProvider

System.Web.Providers.DefaultProfileProvider

System.Web.Security.SqlRoleProvider

System.Web.Providers.DefaultRoleProvider

(Built into default provider)

System.Web.Providers.DefaultSessionStateProvider

In the web.config file, the configuration looks like the following example (the connection string has been wrapped for readability). The differences from the configuration for older SQL-based providers are highlighted. Notice that a section has been added to define custom session-state handling using a custom provider, as described later under Storing Data in Session State using ASP.NET Universal Providers.

 
<configuration>
<connectionStrings>
  <add name="DefaultConnection"
    connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=aspnetdb;Integrated Security=True"
    providerName="System.Data.SqlClient" />
  </connectionStrings>
 
  <system.web>
    <membership defaultProvider="DefaultMembershipProvider">
      <providers>
        <clear />
        <add name="DefaultMembershipProvider"
             type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
             connectionStringName="DefaultConnection"
             enablePasswordRetrieval="false"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="false"
             requiresUniqueEmail="false"
             maxInvalidPasswordAttempts="5"
             minRequiredPasswordLength="6"
             minRequiredNonalphanumericCharacters="0"
             passwordAttemptWindow="10"
             applicationName="/" />
      </providers>
    </membership>
 
    <profile defaultProvider="DefaultProfileProvider">
      <providers>
        <clear />
        <add name="DefaultProfileProvider"
             type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
             connectionStringName="DefaultConnection"
             applicationName="/" />
      </providers>
    </profile>
 
    <roleManager defaultProvider="DefaultRoleProvider" enabled="false">
      <providers>
        <clear />
        <add name="DefaultRoleProvider"
             type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
             connectionStringName="DefaultConnection"
             applicationName="/" />
      </providers>
    </roleManager>
 
    <sessionState mode="Custom" customProvider="DefaultSessionProvider">
      <providers>
        <add name="DefaultSessionProvider"
           type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
           connectionStringName="DefaultConnection"
           applicationName="/" />
      </providers>
    </sessionState>
  </system.web>

Selecting a Data Store

By default, the NuGet package sets the connection string to use a SQL Server Express database (wrapped here for readability):

"Data Source=.\SQLEXPRESS;Initial Catalog=aspnetdb;Integrated Security=True" providerName="System.Data.SqlClient" 

If you want to use SQL Server Compact, change the connection string as shown in the following example:/o:p>

 
<connectionStrings>
  <add name="DefaultConnection" connectionString="Data Source=|DataDirectory|\aspnet.sdf" 
      providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>

If you want to use SQL Azure, change the connection string as shown in the following example (wrapped for readability):

<connectionStrings>
  <add name="DefaultConnection" 
     connectionString="data source=myDNSName;
         User ID=myUserName;Password=myPassword;
         Encrypt=true;Trusted_Connection=false=false"
     providerName="System.Data.SqlClient"/>
<connectionStrings>

Storing Data in Session State using ASP.NET Universal Providers

By default, ASP.NET stores session data using an in-process (in-memory) session provider. This provider allows you to put any object in session state, because session state simply holds a reference to the object, not the object itself.

However, cloud environments might run your application on multiple computers. Therefore, for cloud-based applications, the application must store session state in some form of storage (like a database) that be accessed by more than one machine. This puts some restrictions on what data you store in session state — essentially, the data must be serializable.

When you install ASP.NET Universal Providers, the installation process configures session state to use the System.Web.Providers.DefaultSessionStateProvider type, as shown in the web.config file example earlier. This type stores session state in a database.

Session data must be serializable. If you attempt to store something in session state that is not serializable, you will receive the following error:

Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. the same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode.

There are two ways to resolve this issue: by marking the type as serializable or by using a surrogate serializer.

Marking Types as Serializable

If you have access to the source code for the type that is being stored in session state, you can mark the type using the Serializable attribute, as in the following example. If the type contains additional classes, all the contained classes must be serializable as well.

[Serializable]
public class Address { }
 
[Serializable]
public class Person { 
     public Address Work;
     public Address Home; 
}

Using a Surrogate Serializer in .NET Framework 4.5

If it's not practical to mark the type as serializable in source code, and if you are using .NET Framework 4.5, you can use a surrogate serializer. (This technique does not work in .NET Framework 4.)

Create a class that implements the ISerializationSurrogate interface.  In this class, you implement GetObjectData and SetObjectData methods in order to serialize and deserialze the data, respectively. In GetObjectData you invoke SerializationInfo.AddValue (using the appropriate overload for the data type of your data) to add individual fields of the object to serialize to a SerializationInfo object. In SetObjectData you extract the serialized version back to its original value in the object. Here's an example:

public class EmployeeSerializationSurrogate : ISerializationSurrogate
{
    // Serialize the Employee object to save the object name and address fields.
    public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context)
    {
        Employee emp = (Employee)obj;
        info.AddValue("name", emp.name);
        info.AddValue("address", emp.address);
    }
 
    // Deserialize the Employee object to set the object name and address fields.
    public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context,
        ISurrogateSelector selector)
    {
        Employee emp = (Employee)obj;
        emp.name = info.GetString("name");
        emp.address = info.GetString("address");
        return null;
    }
}

You then register the serializer and the class to be serialized using the SurrogateSelector class in code, like this:

protected void Page_Load(object sender, EventArgs e)
{
    SurrogateSelector ss = new SurrogateSelector();
    ss.AddSurrogate(typeof(Employee), new StreamingContext(StreamingContextStates.All), new EmployeeSerializationSurrogate());
    SessionStateUtility.SerializationSurrogateSelector = ss;
}

Deploying to a Cloud Environment

If you are deploying to a cloud environment that has multiple web server instances, you should change session state mode from "InProc" to "Custom".  In addition, change the connection string named "DefaultConnection" to connect to an instance of SQL Server (including SQL Azure and SQL  Compact) instead of to SQL Server Express.

Known Issues

·         During the installation process, NuGet performs an exact match on items in the web.config file. If you have customized any of the elements in web.config that are updated by the installation process for ASP.NET Universal Providers, the installation process will not find these elements. Instead of updating the elements, the installation process will assume that they do not exist and add them. As a result, the web.config will effectively contain duplicate elements.

If your application is experiencing issues that indicate problems in the web.config file, make sure that the file contains the elements that are illustrated earlier in this document and remove any duplicate elements.

·         If you are working in Visual Studio and use Server Explorer to open the database that contains the membership, role, profile, or session provider tables, and if you then try to run the application, you might see an error like the following:

Cannot open database "aspnet" requested by the login. The login failed. Login failed for user 'yourname.'

To resolve this issue, right-click the database in Server Explorer and then click Close Connection.

·         The new providers do not use the same tables, stored procedures, and database objects as the SQL-based providers. If you switch to the new providers in an existing application, the data from the existing tables will not be available. We are considering creating a conversion utility to help you move existing applications to the new providers.

·         Web Parts personalization will not work with Universal Providers. You must use the SQL Personalization provider. That in turn does not work with LocalDB, so you must use SQL Server Express.

·         This release requires that the ASP.NET application be running under full trust.

Additional Resources

·         Managing Users by Using Membership

·         Managing Authorization Using Roles

·         ASP.NET Profile Providers

·         Microsoft SQL Azure

·         Microsoft SQL Server Compact

·         Configuration File and Source Code Transformations

Disclaimer

This is a preliminary document and may be changed substantially prior to final commercial release of the software described herein.

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT.

Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, email address, logo, person, place or event is intended or should be inferred.

© 2011 Microsoft Corporation. All rights reserved.

Microsoft and Windows are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their respective owners.